﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace DomainDriver.DomainModeling.DomainModels.ConstructionResources
{
    internal class ConstructorCache
    {
        #region Static Members

        internal static readonly ConstructorCache Current = new ConstructorCache();

        #endregion

        #region Members

        private object m_LockObject = new object();
        private Dictionary<string, ConstructorCacheEntry> m_CacheEntries = new Dictionary<string, ConstructorCacheEntry>();

        #endregion

        #region Properties

        internal HashSet<string> EntryKeys
        {
            get { return new HashSet<string>(m_CacheEntries.Keys); }
        }

        #endregion

        #region Methods

        internal ConstructorCacheEntry GetEntry(Type keyType_DotNet, Type keyType_Serialized, Type domainObjectType)
        {
            var key = ConstructorCacheEntry.GetKey(keyType_DotNet, keyType_Serialized, domainObjectType);
            return GetEntry(key);
        }

        internal ConstructorCacheEntry GetEntry(string entryKey)
        {
            lock (m_LockObject)
            {
                if (!m_CacheEntries.ContainsKey(entryKey))
                { return null; }
                return m_CacheEntries[entryKey];
            }
        }

        internal ConstructorCacheEntry SetConstructors_Transient(Type keyType_DotNet, Type keyType_Serialized, Type domainObjectType, ConstructorInfo repositoryConstructor, ConstructorInfo storageManagerConstructor)
        {
            lock (m_LockObject)
            {
                var key = ConstructorCacheEntry.GetKey(keyType_DotNet, keyType_Serialized, domainObjectType);
                var entry = (ConstructorCacheEntry)null;

                if (m_CacheEntries.ContainsKey(key))
                {
                    entry = m_CacheEntries[key];
                }
                else
                {
                    entry = new ConstructorCacheEntry(keyType_DotNet, keyType_Serialized, domainObjectType);
                    m_CacheEntries.Add(entry.EntryKey, entry);
                }

                entry.RepositoryConstructor_Transient = repositoryConstructor;
                entry.StorageManagerConstructor_Transient = storageManagerConstructor;
                return entry;
            }
        }

        internal ConstructorCacheEntry SetConstructors_DataSet(Type keyType_DotNet, Type keyType_Serialized, Type domainObjectType, ConstructorInfo repositoryConstructor, ConstructorInfo storageManagerConstructor)
        {
            lock (m_LockObject)
            {
                var key = ConstructorCacheEntry.GetKey(keyType_DotNet, keyType_Serialized, domainObjectType);
                var entry = (ConstructorCacheEntry)null;

                if (m_CacheEntries.ContainsKey(key))
                {
                    entry = m_CacheEntries[key];
                }
                else
                {
                    entry = new ConstructorCacheEntry(keyType_DotNet, keyType_Serialized, domainObjectType);
                    m_CacheEntries.Add(entry.EntryKey, entry);
                }

                entry.RepositoryConstructor_DataSet = repositoryConstructor;
                entry.StorageManagerConstructor_DataSet = storageManagerConstructor;
                return entry;
            }
        }

        internal ConstructorCacheEntry SetConstructors_EntityFramework(Type keyType_DotNet, Type keyType_Serialized, Type domainObjectType, ConstructorInfo repositoryConstructor, ConstructorInfo storageManagerConstructor)
        {
            lock (m_LockObject)
            {
                var key = ConstructorCacheEntry.GetKey(keyType_DotNet, keyType_Serialized, domainObjectType);
                var entry = (ConstructorCacheEntry)null;

                if (m_CacheEntries.ContainsKey(key))
                {
                    entry = m_CacheEntries[key];
                }
                else
                {
                    entry = new ConstructorCacheEntry(keyType_DotNet, keyType_Serialized, domainObjectType);
                    m_CacheEntries.Add(entry.EntryKey, entry);
                }

                entry.RepositoryConstructor_EntityFramework = repositoryConstructor;
                entry.StorageManagerConstructor_EntityFramework = storageManagerConstructor;
                return entry;
            }
        }

        #endregion
    }

    internal class ConstructorCacheEntry
    {
        internal ConstructorCacheEntry(Type keyType_DotNet, Type keyType_Serialized, Type domainObjectType)
        {
            KeyType_DotNet = keyType_DotNet;
            KeyType_Serialized = keyType_Serialized;
            DomainObjectType = domainObjectType;

            RepositoryConstructor_Transient = null;
            StorageManagerConstructor_Transient = null;
        }

        internal Type KeyType_DotNet { get; private set; }
        internal Type KeyType_Serialized { get; private set; }
        internal Type DomainObjectType { get; private set; }

        internal string EntryKey { get { return GetKey(KeyType_DotNet, KeyType_Serialized, DomainObjectType); } }

        internal bool HasConstructor_Transient { get { return ((RepositoryConstructor_Transient != null) && (StorageManagerConstructor_Transient != null)); } }
        internal ConstructorInfo RepositoryConstructor_Transient { get; set; }
        internal ConstructorInfo StorageManagerConstructor_Transient { get; set; }

        internal bool HasConstructor_DataSet { get { return ((RepositoryConstructor_DataSet != null) && (StorageManagerConstructor_DataSet != null)); } }
        internal ConstructorInfo RepositoryConstructor_DataSet { get; set; }
        internal ConstructorInfo StorageManagerConstructor_DataSet { get; set; }

        internal bool HasConstructor_EntityFramework { get { return ((RepositoryConstructor_EntityFramework != null) && (StorageManagerConstructor_EntityFramework != null)); } }
        internal ConstructorInfo RepositoryConstructor_EntityFramework { get; set; }
        internal ConstructorInfo StorageManagerConstructor_EntityFramework { get; set; }

        internal static string GetKey(Type keyType_DotNet, Type keyType_Serialized, Type domainObjectType)
        {
            return (keyType_DotNet.ToString() + "," + keyType_Serialized.ToString() + "," + domainObjectType.ToString());
        }
    }
}